#include "ContentManager.h"

// Forward Declarations
std::vector<Texture2D*> ContentManager::Texture2DCache;

// Prepares ContentManager for use
bool ContentManager::Initialize()
{
    #if defined(_DEBUG)
    std::cout << "ContentManager initialized." << std::endl;
    #endif
    return true;
}

// Loads a Texture2D file for use
template<> Texture2D* ContentManager::LoadContent<Texture2D>(std::string fileName)
{
    for(unsigned int i = 0; i < Texture2DCache.size(); i++)
        if(Texture2DCache[i]->fileName == fileName)
            return Texture2DCache[i];
    // File not already loaded
    std::string loadMessage = "Loading texture: ";
    loadMessage.append(fileName);
    loadMessage.append("... ");
    SDL_Surface* surface = IMG_Load(fileName.c_str());
    if(surface == NULL)
    {
         loadMessage.append("Error loading texture.");
         Engine::Instance().log(loadMessage);
         return NULL;
    }
    GLfloat texcoord[4];
    GLuint texture;
    Texture2D* texturePackage = new Texture2D;
    int w, h;
    SDL_Surface *image;
    SDL_Rect area;
    Uint32 saved_flags;
    Uint8  saved_alpha;
    w = surface->clip_rect.x;
    // Use the surface width and height expanded to powers of 2
    w = powerOfTwo(surface->w);
    h = powerOfTwo(surface->h);
    texcoord[0] = 0.0f;                     // Min X
    texcoord[1] = 0.0f;                     // Min Y
    texcoord[2] = (GLfloat)surface->w / w;  // Max X
    texcoord[3] = (GLfloat)surface->h / h;  // Max Y
    image = SDL_CreateRGBSurface(
         SDL_SWSURFACE,
         w, h,
         Engine::Instance().getScreenDepth(),
#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
         0x000000FF,
         0x0000FF00,
         0x00FF0000,
         0xFF000000
#else
         0xFF000000,
         0x00FF0000,
         0x0000FF00,
         0x000000FF
#endif
    );
    if (image == NULL)
        return NULL;
    // Save the alpha blending attributes
    saved_flags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
    saved_alpha = surface->format->alpha;
    if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA)
        SDL_SetAlpha(surface, 0, 0);
    // Copy the surface into the GL texture image
    area.x = 0;
    area.y = 0;
    area.w = (int)surface->w;
    area.h = (int)surface->h;
    SDL_BlitSurface(surface, NULL, image, NULL);
    // Restore the alpha blending attributes
    if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA)
        SDL_SetAlpha(surface, saved_flags, saved_alpha);
    //glEnable(GL_TEXTURE_2D); // Enable texture mapping - Should already be enabled!
    // Create an OpenGL texture for the image
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    // Filtering w/o mipmapping:
    // MAG filter = when texture is enlarged
    // MIN filter = when texture is shrinked
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    // Use glTexImage2D instead of gluBuild2DMipmaps if we don't want to use mipmapping for this texture

    // Mipmapping Filtering
    // One benefit of mipmapping is it allows us to use non base 2 source images - it will be autoscalled for us
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); // Mipmapping screws up the 2D pass!
    //gluBuild2DMipmaps(GL_TEXTURE_2D, // <- "Never use this." Source: http://www.opengl.org/wiki/Common_Mistakes#gluBuild2DMipmaps
    glTexImage2D(GL_TEXTURE_2D,
        0,
        GL_RGBA,
        w, h,
        0,
        GL_RGBA,
        GL_UNSIGNED_BYTE,
        image->pixels);
    texturePackage->fileName = fileName;
    texturePackage->ID = texture;
    texturePackage->width = w;
    texturePackage->height = h;
    texturePackage->originalWidth = surface->w;
    texturePackage->originalHeight = surface->h;
    // Clean up
    SDL_FreeSurface(surface);
    SDL_FreeSurface(image);
    // Store the texture reference in our array
    Texture2DCache.push_back(texturePackage);
    loadMessage.append("Done.");
    Engine::Instance().log(loadMessage);
    return texturePackage;
}

// Clears the Texture2D cache
template<> void ContentManager::ClearCache<Texture2D>()
{
    for(unsigned int i = 0; i < Texture2DCache.size(); i++)
        delete Texture2DCache[i];
    ContentManager::Texture2DCache.clear();
}

// Converts an int to the next highest power of two
int ContentManager::powerOfTwo(int input)
{
    int value = 1;
    while (value < input)
        value <<= 1;
    return value;
}
